Elementor is a great Page Builder for WordPress sites. It is really developer friendly and that is why I have decided to give it a try and use it for a client of mine. In this tutorial, I will show you how to add a custom button field and a skin.
While I am refactoring the whole theme and I am using Elementor to replicate hard-coded elements, I am learning a lot. I will also see to write more on this topic so you can learn how to extend Elementor.
Let’s get started. Open the file where you will add the code. That can be in your plugin or theme (I would recommend to put it in a plugin).
Adding a new Button Field in Elementor
To add a new field, I will hook my own function to an action in Elementor. Elementor fields are placed in sections. You can see almost in all elements (widgets) there is a layout or content section, then a style section and maybe an advanced section.
This is the hook name:
'elementor/element/' . $this->get_name() . '/' . $section_id . '/after_section_start'
If we are to target our Button element and a Style section, we will need to hook our function here:
'elementor/element/button/section_style/after_section_start'
When hooking to that location, we are placing our field at the beginning. If you want to place your field at the end of the section, you can use the suffix before_section_end. Let’s now add this code:
The action hooks mentioned before, are passing the current object ($this
) and arguments of that section. To add a new field, we will use the method add_control
and pass the parameters to define our field.
When defining a field type, you will need to call that from the Elementor namespacephp.net. If you’re not familiar with namespaces, you can read about them on php.net. The values of our dropdown will be used as new classes.
Rendering the Field value
To manipulate the rendering of Elementor elements such as widgets, you can use two general hooks:
elementor/widget/before_render_content
– Action hook before the rendering startedelementor/widget/render_content
– Filter hook to change the rendered HTML
If elements have other hooks or filters within their rendering methods, you can also use those. For our example, we are adding a class, so we will just use the action hook before the rendering started. Within that hook we will use a method add_render_attribute
which will queue our attribute until the button rendering starts.
The first thing we do here is to check if the element which is passed is a button. That is done by using the method get_name()
. If the current element is not a button, we won’t do anything.
If it is a button, then we will get the settings, check the custom field value and then add it as a class attribute to button. We need to pass button
because the whole button is wrapped around an elementor wrapper
. If we want to add that attribute to the wrapper instead, we would put wrapper
instead of button
.
When rendering, Elementor will check all the attribute values passed to the button
and class
and add those values as classes.
Now, if you have a style defined for the selected class, your button output will change. But, your button style won’t change as you change that value.
That is because the button has a print template defined which is a JavaScript Backbone.js template that is used to perform live updates on the element.
How to do that? We will learn that as we learn how to add a new Skin. You don’t have to use a Skin for that, but I’ll explain it there so I don’t repeat myself.
Adding a Custom Elementor Skin
To add a skin, we will need to use the abstract class Skin_Base
which is also under the namespace Elementor
, so we will have to use a full path Elementor\Skin_Base
. To register a skin for an element, we need to use the hook:
elementor/widget/' . $this->get_name() . '/skins_init
Since we are talking about a Button element, we will use:
elementor/widget/button/skins_init
Let’s do that now:
Simple as that. But, it won’t work. We don’t have the class Custom_Button_Skin
defined. We need to define that classs before that hook. Otherwise it won’t find that class definition.
But, we can’t just define that class in our file. What if Elementor is deactivated? Our code will break. We will define that class once Elementor has been initialized. It might seem like a big one, but bear with me.
The part that we actually need to understand is the render()
method. Inside that method, we are making almost an exact copy of the button element. But with a big difference.
The difference happens when adding attributes. Since Skin is just an overlay of our Button, we don’t have such methods there. But we do have a class attribute $parent
which references our Button. That’s why we are using $this->parent->add_render_attribute()
. The method render_text()
is not required. That is just a helper method used inside the Button element so I have just copied it out and changed $this->add_render_attribute()
to $this->parent->add_render_attribute()
.
Now, within the render()
method, I have full control of the output. On line 42, I am defining a $button_type
. If that variable has a value of no
then I won’t add it as a class.
And I have also made another change (on line 55) and that is removing the Elementor size class if we have our own custom button type. That’s how I am removing any styles inherited by the Elementor size class.
Changing the Live Preview Template
Both of the scenarios above will work, but only if we refresh the page. We won’t see those changes appear as we change them. That is done through a method print_template()
which is not available through Skins. So we can’t change them just by defining the method print_template()
inside our Skins. That is why, you can easily change the live preview templates without Skins (they are not required).
But, let’s now add that option in our Skin. In our __construct()
method, we will add a filter and we will also define a new method.
As you might have read, if we just return an empty string, Elementor will use our render()
method instead and do a page reload on each change. But let’s be a bit more advanced and learn about Backbone.js templating :).
Again, since this is a generic filter and not a filter where we could just target our button, we need to change if the current element is the Button element. Let’s now define our _content_template()
method!
Our method is just a copy of the method inside the Button element with a simple adjustment. Everything remains the same except the classes. I will break the change in lines so it’s easier to read.
<# if( 'no' === settings.custom_button_type ) { #> elementor-size-{{ settings.size }} <# } else { #> {{ settings.custom_button_type }} <# } #>
So, what are we doing here? You can feel about the <#
and #>
as PHP tags <?php
and ?>
. We are opening them so we can type JavaScript inside of them. We are then checking if the value of our field is no
. If it is, then we are just using the default sizing class from Elementor. If it’s something else, we are putting that as a class. The parentheses {{
and }}
are the same as saying echo
in PHP. If you have HTML tags inside of your variable, then you should use {{{
and }}}
.
Conclusion
Elementor is a great page builder and with so many actions and filters, you can really do a lot of them. The next step which I would like to teach you about is how to create your own custom elements in Elementor.
We could have escaped the custom rendering method and so on if we could also have a way to remove such attributes. Maybe the folks from Elementor, if they read this, will consider adding that 😀
Have you tried building custom elements in Elementor or extending it in any way? Please share in the comments below so I can also learn even more;)
This part is available only to the members. If you want to become a member and support my work go to this link and subscribe: Become a Member
Hello, Thanks for your tutorial can you explain how to write javascript for that element need to initialize after clone? for example please look here: https://youtu.be/SujRn_DGgc8
my document ready code is: https://pastebin.com/aJiEx0XD
Please help me about elementor javascript API
Hi Selim, I haven’t tried it but I will try to help you. In my opinion, you should have a separate JavaScript file for the editor as well. Enqueue that file by using the hook
elementor/editor/before_enqueue_scripts
instead ofwp_enqueue_scripts
. That will load after the Elementor editor scripts. Then inside that script, you can attach an event onelement:after:duplicate
. Something like$(document).on('element:after:duplicate', function( model ) { Do something with the model });
. Be sure to check how Elementor does that insideelementor/assets/js/editor.js
.Hello Igor,
I tried your way but it’s not working 🙁
jQuery(document).on(‘element:after:duplicate’, function( model ) {
console.log(‘duplicated’);
});
console.log(‘i am from bottom of editor.js’);
I wrote it and and loaded it in editor mode. I just get last log but not coming from duplicated method 🙁
By the way i chekced the editor js from elementor plugin folder but don’t understand
it (I have lack in javascript), maybe it’s compiled by grunt js.
Hi Selim, you can try using this:
The JavaScript you see there is normal and unimified just a new way of writing it. Check ES6 for that.
showing examples would be nice.
What do you mean by examples? As images or something else? 🙂
I’m still reading through the post, but might as well leave a comment to see if you can help. I’m trying to extend the Image widget so that I can add extra Pinterest attribute fields to it: https://css-tricks.com/using-pinterest-data-attributes-and-meta-tags/
The problem with Elementor Pro’s current attribute manager is that it adds attributes to the wrapper and not the actual element.
Hi Tanner,
when elementor renders the widget Image, it filters the HTML with
return apply_filters( 'elementor/image_size/get_attachment_image_html', $html, $settings, $image_size_key, $image_key );
You can try hooking into that filter and see if you have the $settings attribute there. Then, you can use
str_replace
function to replace ‘img ‘ with ‘img attribute=”name”‘ and add attributes you need.Hello Igor –
I’m looking to do something similar by adding/replacing the button type names (and maybe classes) like info/warning/danger/success, with something more descriptive and pertaining to button styles I’m creating in the child theme.
Is there a function I can add to my child theme that would allow me to do this?
Hi Sean,
you can use the method remove_control on the button or any elementor widget to remove a control and then add the one you need. Maybe, by using the same ids, you could still have everything working out of the box but with your own classes.
Hello Igor Benic,
Thanks much for nice post. Could you please tell me how edit control of exits widget?
For ex: Edit options list or default value of button type.
Hi Steve, I’ve not tried editing the controls of such widget. I would recommend you to open the code of that widget and also see if there are any filters that could help you do that.
This tutorial should be able to get you an idea on how to do that or where to look at.
Hi Igor,
Thank you for your helpful post. I want to add an attribute in woocommerce add-to-cart button. I tried the hook ‘elementor/element/button/section_style/before_section_end’. It works on basic buttons rather than on the add-to-cart buttons from woocommerce widget. Where can I find the section name and section id of the hook to add an attribute in the woocommerce button?
Hi Alice, the WooCommerce add-to-cart button is not an Elementor element. This is a WooCommerce one and you can find how they build it in the WooCommerce plugin folder under
templates/single-product/add-to-cart
Hey, Thank you for sharing these images. It gives a perfect example of customization of the button field. This helps me to create a beautiful website.
I have Controls_Manager :: SELECT, which “switches” block design presets.
It looks like:
“`
‘options’ => [
‘1’ => __( ‘Style 1’, ‘plugin’ ),
‘2’ => __( ‘Style 2’, ‘plugin’ ),
‘3’ => __( ‘Style 3’, ‘plugin’ ),
‘6’ => __( ‘Style 6’, ‘plugin’ ),
],
“`
The value is passed in $ params [‘style’]
“`
include( plugin_dir_path( __FILE__ ) . ‘../templates/content-style-‘.$params[‘style’].’.php’ );
“`
and thus switching presets.
For example, for Style 1 we set the blue background, then switch to Style 2.
The background of the block remains blue. How to reset it to default when switching “presets”?
Great article! Do you have any experience with removing certain controls from default Elementor widgets?
I do not, sorry 🙂
Hi Igor,
Thank you very much for the post. I was wondering if you could please point me in the right direction. I am trying to modify or create my own add to cart widget to customize it in the way I desire.
I would like to change the quantity input to a dropdown control and position this before the add to cart button and not to the left side as it is right now.
I now you are too busy, I was wondering if you now any of the following a video , a course, a traing, a code example that could help me to have the right idea.
Best Regards
Hi Nestor, is that a WooCommerce widget?
Hey, I was wondering if this can help me to extend an existing widget, specifically the Post widget, because I have created a custom field where I will be adding a shortcode, so I would like to display it using the Post widget but can’t find how to do it.
Thanks!
Not sure the Post widget can be used for that. Unless the front of the post widget has actions where you can hook to check. If you’re talking about the Elementor Post widget, then it could have some actions there that can be used to add new features or display other things on the front. But I have not yet tried that.
Hy Igor,
thanks for the post, I Have a question:
How do i include the css file only if the skin is active…
1. i enqueue it n the skin constructor. Sideeffect: when the widget is included it load this css also.
2. i enqueue it in the render function. Sideeffect: the css is loaded only when ski active, but only in the frontend. Not in the backend.
i tried to add action in constructor on ‘elementor/editor/before_enqueue_scripts’ but it doesn’t work. MAybe it’s too late for that.
Hi, the problem here is that I am not sure if Elementor provides a way to enqueue based on skin. I know you can enqueue styles based on elements that are used. So, it might be that the skin also has a method like that. Can’t be for sure though as I can’t currently check that out.